#include "nxdat_json_io.h"
#include "utils.h"

namespace dx {
	/*basic format style is followings
	[
		{
			"name":'name',
			"tags":['tag1','tag2'],
			"meta":"meta1",
			"type":"int|real|vector2|vector3|vector4|string|wstring|node|list|map|table",
			"cell":"value"|[v1,v2,v3],
			"children":[
				
			]
		}
	]
	*/
	class c_dx_io_json_write_t
	{
	public:
		c_dx_io_json_write_t(dx_stream_write_t* stream,dx_node_t* node):
			_stream(stream)
		{
			write(node, 0);
		}
	private:
		void writeBuffer(void* buf, int l)
		{
			_stream->write(_stream->handle, buf, l);
		}
		void write_indent_tab(int indent)
		{
			for (int i = 0; i < indent; i++)
			{
				writeBuffer("\t", 1);
			}
		}
		void write_buffer_with_indent_tab(void* buf, int l, int indent)
		{
			write_indent_tab(indent); writeBuffer(buf, l);
		}

		void write_cell_int(dx_cell_t& cell)
		{
			static char buf[256];
			if (cell.length==1)
			{
				itoa(cell.iVal[0], buf, 10);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if(i>0)writeBuffer(",",1);
					itoa(cell.iVal[i], buf, 10);
					writeBuffer(buf, strlen(buf));
				}
				writeBuffer("]", 1);
			}
		}

		void write_cell_real(dx_cell_t& cell)
		{
			static char buf[256];
			if (cell.length == 1)
			{
				sprintf(buf,"%f",cell.rVal[0]);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);
					sprintf(buf, "%f", cell.rVal[i]);
					writeBuffer(buf, strlen(buf));
				}
				writeBuffer("]", 1);
			}
		}
		void write_cell_vector2(dx_cell_t& cell)
		{
			static char buf[256];
			if (cell.length == 1)
			{
				sprintf(buf, "[%f,%f]", cell.vec2[0].x,cell.vec2[0].y);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);
					sprintf(buf, "[%f,%f]", cell.vec2[i].x, cell.vec2[i].y);
					writeBuffer(buf, strlen(buf));
				}
				writeBuffer("]", 1);
			}
		}
		void write_cell_vector3(dx_cell_t& cell)
		{
			static char buf[256];
			if (cell.length == 1)
			{
				sprintf(buf, "[%f,%f,%f]", cell.vec3[0].x, cell.vec3[0].y, cell.vec3[0].z);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);
					sprintf(buf, "[%f,%f,%f]", cell.vec3[i].x, cell.vec3[i].y, cell.vec3[i].z);
					writeBuffer(buf, strlen(buf));
				}
				writeBuffer("]", 1);
			}
		}

		void write_cell_vector4(dx_cell_t& cell)
		{
			static char buf[256];
			if (cell.length == 1)
			{
				sprintf(buf, "[%f,%f,%f,%f]", cell.vec4[0].x, cell.vec4[0].y, cell.vec4[0].z, cell.vec4[0].w);
				writeBuffer(buf, strlen(buf));
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);	
					sprintf(buf, "[%f,%f,%f,%f]", cell.vec4[0].x, cell.vec4[0].y, cell.vec4[0].z, cell.vec4[0].w);
					writeBuffer(buf, strlen(buf));
				}
				writeBuffer("]", 1);
			}
		}

		void write_cell_string(dx_cell_t& cell)
		{
			if (cell.length == 1)
			{
				writeBuffer("\"",1);
				writeBuffer((void*)cell.str,strlen((char*)cell.str));
				writeBuffer("\"", 1);
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					writeBuffer((void*)cell.pstr[i], strlen((char*)cell.pstr[i]));
					writeBuffer("\"", 1);
				}
				writeBuffer("]", 1);
			}
		}

		void write_cell_wstring(dx_cell_t& cell)
		{
			if (cell.length == 1)
			{
				writeBuffer("\"", 1);
				string utf8 = utf16_to_utf8(cell.wstr);
				writeBuffer((void*)utf8.c_str(), utf8.size());
				writeBuffer("\"", 1);
			}
			else
			{
				writeBuffer("[", 1);
				for (int i = 0; i < cell.length; i++)
				{
					if (i>0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					string utf8 = utf16_to_utf8(cell.pwstr[i]);
					writeBuffer((void*)utf8.c_str(), utf8.size());
					writeBuffer("\"", 1);
				}
				writeBuffer("]", 1);
			}
		}

		void write_data_cell(dx_cell_t& cell)
		{
			if (cell.type == dxint)
				write_cell_int(cell);
			else if (cell.type == dxreal)
				write_cell_real(cell);
			else if (cell.type == dxvector2)
				write_cell_vector2(cell);
			else if (cell.type == dxvector3)
				write_cell_vector3(cell);
			else if (cell.type == dxvector4)
				write_cell_vector4(cell);
			else if (cell.type == dxstring)
				write_cell_string(cell);
			else if (cell.type == dxwstring)
				write_cell_wstring(cell);
			else
			{
				_ASSERT(0);//never will happend
			}
		}

		void write(dx_node_t* node,int indent)
		{
			write_buffer_with_indent_tab("{\n",2,indent);

			write_buffer_with_indent_tab("\"name\":\"",8, indent+1);//name
			nxid_t name = dxutils::get_name(node);
			writeBuffer((void*)name, strlen(name));
			writeBuffer("\",\n", 3);

			if (dxutils::get_tag_count(node) > 0)
			{
				write_buffer_with_indent_tab("\"tags\":\"", 8, indent + 1);//tags
				writeBuffer("[", 1);
				for (int i = 0, n = dxutils::get_tag_count(node); i < n; i++)
				{
					nxid_t tg = dxutils::get_tag(node, i);
					if (i > 0)writeBuffer(",", 1);
					writeBuffer("\"", 1);
					writeBuffer((void*)tg, strlen(tg));
					writeBuffer("\"", 1);
				}
				writeBuffer("],\n", 3);
			}

			if (dxutils::get_meta(node) != NULL)//meta
			{
				const dx_node_meta_t* meta = dxutils::get_meta(node);
				
				write_buffer_with_indent_tab("\"meta\":\"", 8,indent + 1);
				writeBuffer((void*)meta->name, strlen(meta->name));
				writeBuffer("\",\n", 3);
			}
			write_buffer_with_indent_tab("\"type\":\"", 8, indent + 1);//type
			e_dx_type_t ty = dxutils::get_type(node);
			const char* tystr = dx_type_to_string(ty);
			writeBuffer((void*)tystr,strlen(tystr));
			writeBuffer("\",\n", 3);

			if (dxutils::is_data_cell(node))
			{
				write_buffer_with_indent_tab("\"cell\":", 7, indent + 1);//type
				write_data_cell(dxutils::get_data_cell(node));
			}
			else
			{	
				write_buffer_with_indent_tab("\"children\":[\n", 13, indent + 1);//type
				for (int i = 0, n = dxutils::get_length(node); i < n; i++)
				{
					if (i > 0)writeBuffer(",\n", 2);
					write(dxutils::get_child(node, i), indent + 1);
					
				}
				writeBuffer("\n]", 2);
			}
			writeBuffer("\n", 1);
			write_buffer_with_indent_tab("}",1,indent);
		}
	private:
		dx_stream_write_t*		_stream;
	};

	//read json
	class c_dx_io_json_read_t
	{
	public:
		c_dx_io_json_read_t(dx_stream_read_t* stream, dx_node_t* parent):_stream(stream)
		{
			string  strDoc;
			char	buf[1025];
			while (stream->read(stream->handle,(void*)buf, 1024)>0)
			{
				buf[1024]=0;
				strDoc += string(buf);
			}
			Json::Reader	reader;
			Json::Value		root;

			if (!reader.parse(strDoc, root))
			{
				string msg = reader.getFormatedErrorMessages();
			}
			else
			{
				_node = read(parent, root);
			}
		}
	private:
		void read_cell_int(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
				cell.iVal[0] = val.asInt();
			else
			{
				for (int i = 0;i<cell.length;i++)
				{
					cell.iVal[i] = val[i].asInt();
				}
			}
		}

		void read_cell_real(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
				cell.rVal[0] = val.asDouble();
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					cell.rVal[i] = val[i].asDouble();
				}
			}
		}

		void read_cell_vector2(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
			{ 
				cell.vec2[0].x = val[0].asDouble();
				cell.vec2[0].y = val[1].asDouble();
			}
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					Json::Value xy = val[i];
					cell.vec2[i].x = xy[0].asDouble();
					cell.vec2[i].y = xy[1].asDouble();
				}
			}
		}		
		void read_cell_vector3(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
			{
				cell.vec3[0].x = val[0].asDouble();
				cell.vec3[0].y = val[1].asDouble();
				cell.vec3[0].z = val[2].asDouble();
			}
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					Json::Value xyz = val[i];
					cell.vec3[i].x = xyz[0].asDouble();
					cell.vec3[i].y = xyz[1].asDouble();
					cell.vec3[i].z = xyz[2].asDouble();
				}
			}
		}
		void read_cell_vector4(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
			{
				cell.vec4[0].x = val[0].asDouble();
				cell.vec4[0].y = val[1].asDouble();
				cell.vec4[0].z = val[2].asDouble();
				cell.vec4[0].w = val[3].asDouble();
			}
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					Json::Value xyzw = val[i];
					cell.vec4[i].x = xyzw[0].asDouble();
					cell.vec4[i].y = xyzw[1].asDouble();
					cell.vec4[i].z = xyzw[2].asDouble();
					cell.vec4[i].w = xyzw[3].asDouble();
				}
			}
		}

		void read_cell_string(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
			{
				string str = val.asString();
				dxutils::put_string(cell.node, str.c_str(), 0);
			}
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					string str = val[i].asString();
					dxutils::put_string(cell.node, str.c_str(),i);
				}
			}
		}

		void read_cell_wstring(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.length == 1)
			{
				string str = val.asString();
				wstring wstr = utf8_to_utf16(str);

				dxutils::put_wstring(cell.node, wstr.c_str(), 0);
			}
			else
			{
				for (int i = 0; i<cell.length; i++)
				{
					string str = val[i].asString();
					wstring wstr = utf8_to_utf16(str);
					dxutils::put_wstring(cell.node, wstr.c_str(), i);
				}
			}
		}

		void read_data_cell(dx_cell_t& cell, Json::Value& val)
		{
			if (cell.type == dxint)
				read_cell_int(cell, val);
			else if (cell.type == dxreal)
				read_cell_real(cell, val);
			else if (cell.type == dxvector2)
				read_cell_vector2(cell, val);
			else if (cell.type == dxvector3)
				read_cell_vector3(cell, val);
			else if (cell.type == dxvector4)
				read_cell_vector4(cell, val);
			else if (cell.type == dxstring)
				read_cell_string(cell, val);
			else if (cell.type == dxwstring)
				read_cell_wstring(cell, val);
		}

		dx_node_t*  read(dx_node_t* parnode, Json::Value& jsnode)
		{
			nxid_t name = NULL;
			const dx_node_meta_t* meta=NULL;
			vector<nxid_t> tags;
			e_dx_type_t   ty = dxnull;
			{
				static string kname("name");
				Json::Value v = jsnode.get(kname,Json::Value::null);
				_ASSERT(!v.isNull());
				string strname = v.asString();
				name = nxid(strname.c_str());
			}
			{//	
				static string kmeta("meta");
				Json::Value v = jsnode.get(kmeta, Json::Value::null);
				if(!v.isNull());
				{
					string str = v.asString();
					nxid_t metaid = nxid(str.c_str());
					//TODO:get meta lib
				}
			}
			{
				static string ktag("tags");
				Json::Value v = jsnode.get(ktag, Json::Value::null);
				if (!v.isNull())
				{
					if (v.isString())
					{
						string str = v.asString();
						tags.push_back(nxid(str.c_str()));
					}
					else
					{
						for (int i = 0, n = v.size(); i < n; i++)
						{
							string str = v[i].asString();
							tags.push_back(nxid(str.c_str()));
						}
					}
				}
			}
			{
				static string ktype("type");
				Json::Value v = jsnode.get(ktype, Json::Value::null);
				_ASSERT(!v.isNull());
				string str = v.asString();
				ty = parse_dx_type(str.c_str());
				_ASSERT(ty != dxnull);
			}
			if (ty< dxnode)
			{
				static string kcell("cell");
				int length = 1;
				Json::Value v = jsnode.get(kcell, Json::Value::null);
				if (v.isArray())
					length = v.size();
				dx_node_t* nn = dxutils::add_child(parnode, name, ty, length);
				read_data_cell(dxutils::get_data_cell(nn), v);
				return nn;
			}
			else
			{
				dx_node_t* nn = dxutils::add_child(parnode, name, ty, 0);

				static string kcell("children");
				
				Json::Value v = jsnode.get(kcell, Json::Value::null);
				if (v.isArray())
				{
					int length = v.size();
					for (int i = 0; i < length; i++)
						read(nn, v[i]);
				}
				else
				{
						read(nn, v);
				}
				return nn;
			}
		}
	public:
		dx_stream_read_t*		_stream;
		dx_node_t*				_node;
	};
}
	extern "C" {
		NXDAT_MINI_API
			int dx_write_node_to_json(dx_stream_write_t* stream, dx_node_t* node)
		{
			dx::c_dx_io_json_write_t write(stream, node);
			return -1;// we don't known how much we have writen!
		}
		NXDAT_MINI_API
			dx_node_t* dx_read_node_from_json(dx_stream_read_t* stream, dx_node_t* parent, dx_node_meta_lib_t* metalib)
		{

			dx::c_dx_io_json_read_t read(stream, parent);
			return read._node;// we don't known how much we have writen!
		}
	}